const playwright = require('playwright');
const path = require('path');
const fs = require('fs').promises;
const crypto = require('crypto');
const UrlNormalizer = require('../tools/UrlNormalizer');
const CaptureManager = require('./CaptureManager');
const DomOperations = require('./DomOperations');

/**
 * PageWebScraper 类 - 页面抓取工具
 * 提供灵活的文章列表抓取功能，支持自定义选择器和操作
 */
class PageWebScraper {
    /**
     * @param {Object} options - 配置选项
     * @param {Object} options.browser - 浏览器实例
     * @param {Object} options.captureManager - 抓取管理器实例
     * @param {Object} options.templateVars - 模板变量，用于替换配置中的模板
     */
    constructor(options = {}) {
        this.options = {
            headless: true,
            webDriver: null,
            configPath: path.join(__dirname, '..', 'config', 'page-sites.json'),
            ...options
        };

        // 加载配置文件
        this.loadConfig();

        // 初始化URL规范化工具
        this.urlNormalizer = new UrlNormalizer(options.urlNormalization);
        
        // 初始化内容管理器
        this.captureManager = new CaptureManager(options.captureManager);

        this.browser = null;
        this.context = null;
        this.page = null;
        this.domOps = null;
        // 获取模板变量
        this.templateVars = options.templateVars || {};
    }

    /**
     * 加载配置文件
     */
    async loadConfig() {
        try {
            const configContent = await fs.readFile(this.options.configPath, 'utf-8');
            this.siteConfigs = JSON.parse(configContent);
        } catch (error) {
            console.error('加载配置失败:', error);
            throw error;
        }
    }

    /**
     * 替换配置中的模板变量
     * @param {Object} config - 配置对象
     * @returns {Object} - 替换后的配置对象
     */
    replaceTemplateVars(config) {
        const stringified = JSON.stringify(config);
        const replaced = stringified.replace(/\${(\w+)}/g, (match, key) => {
            if (this.templateVars.hasOwnProperty(key)) {
                return this.templateVars[key];
            }
            console.warn(`模板变量 "${key}" 未定义`);
            return match;
        });
        return JSON.parse(replaced);
    }

    /**
     * 获取站点配置
     * @param {string} url - 目标URL
     * @returns {Object} - 站点配置
     */
    getSiteConfig(url) {
        // 获取网站特定配置
        const siteConfig = Object.entries(this.siteConfigs).find(([pattern]) => {
            if (pattern === 'default') return false;
            return url.includes(pattern);
        });

        if (siteConfig) {
            const config = this.replaceTemplateVars(siteConfig[1]);
            return {
                ...this.siteConfigs.default,
                ...config
            };
        }

        return this.siteConfigs.default;
    }

    /**
     * 生成URL的哈希值
     * @param {string} url - 目标URL
     * @returns {string} - 哈希值
     */
    generateUrlHash(url) {
        const normalizedUrl = this.urlNormalizer.normalize(url);
        return crypto.createHash('md5').update(normalizedUrl).digest('hex');
    }

    /**
     * 检查URL是否已经被抓取
     * @param {string} url - 目标URL
     * @returns {Promise<boolean>} - 是否已存在
     */
    async checkUrlExists(url) {
        const hash = this.generateUrlHash(url);
        const indexPath = path.join(process.cwd(), 'assets', 'indexs', `${hash}.index`);
        
        try {
            await fs.access(indexPath);
            return true;
        } catch {
            return false;
        }
    }

    /**
     * 保存URL索引
     * @param {string} url - 目标URL
     */
    async saveUrlIndex(url) {
        const hash = this.generateUrlHash(url);
        const indexPath = path.join(process.cwd(), 'assets', 'indexs', `${hash}.index`);
        
        try {
            // 确保目录存在
            await fs.mkdir(path.dirname(indexPath), { recursive: true });
            // 创建索引文件
            await fs.writeFile(indexPath, url);
        } catch (error) {
            console.error('保存URL索引失败:', error);
            throw error;
        }
    }

    /**
     * 初始化浏览器
     */
    async initBrowser() {
        if (this.options.webDriver && this.options.webDriver.endpoint) {
            console.log('正在连接到 WebDriver:', this.options.webDriver.endpoint);
            this.browser = await playwright.chromium.connectOverCDP(this.options.webDriver.endpoint);
            this.context = await this.browser.contexts()[0];
        } 
        else {
            this.browser = await playwright.chromium.launch({
                headless: this.options.headless
            });
            this.context = await this.browser.newContext();
        }
        
        this.page = await this.context.newPage();
        this.domOps = new DomOperations(this.page);
    }

    /**
     * 关闭浏览器
     */
    async close() {
        if (this.browser) {
            await this.browser.close();
            this.browser = null;
            this.context = null;
            this.page = null;
        }
    }

    /**
     * 执行动作
     * @param {Object} action - 动作配置
     * @returns {Promise<any>} 动作结果
     */
    async executeAction(action) {
        try {
            switch (action.type) {
                case 'wait':
                    if(action.selector){
                        await this.page.waitForSelector(action.selector, {
                            timeout: action.waitTime || 2000
                        });
                    }else{
                        await this.page.waitForTimeout(action.waitTime || 2000);
                    }
                    break;
                    
                case 'scroll':
                    await this.page.evaluate(() => {
                        window.scrollTo(0, document.body.scrollHeight);
                    });
                    await this.page.waitForTimeout(1000);
                    break;
                    
                case 'click':
                    const elementExists = await this.domOps.checkElementExists(action.selector);
                    if (elementExists) {
                        await this.page.click(action.selector);
                    }
                    break;
                    
                case 'remove':
                    for (const selector of action.selectors) {
                        await this.domOps.handleRemove(selector);
                    }
                    break;
                    
                case 'selectors':
                    const content = {};
                    for (const [key, selectorConfig] of Object.entries(action.rules)) {
                        try {
                            const selector = selectorConfig.selector;
                            const attr = selectorConfig.attr || 'text';
                            const multi = selectorConfig.multi || false;

                            console.log("输出",multi)

                            if (multi) {
                                const elements = await this.page.$$(selector);
                                content[key] = await Promise.all(
                                    elements.map(async element => {
                                        const value = await this.domOps.getElementContent(element, attr);
                                        return value !== null ? value : null;
                                    })
                                );
                                content[key] = content[key].filter(value => value !== null);
                            } else {
                                const targetElement = await this.domOps.getElement(selector);
                                if (targetElement) {
                                    content[key] = await this.domOps.getElementContent(targetElement, attr);
                                }
                            }
                        } catch (error) {
                            console.warn(`Failed to extract ${key}:`, error);
                            content[key] = multi ? [] : null;
                        }
                    }
                    return content;
                    
                case 'screenshot':
                    return await this.executeScreenshotAction(action);
                    
                default:
                    console.warn(`Unknown action type: ${action.type}`);
            }
        } catch (error) {
            console.error(`Failed to execute action ${action.type}:`, error);
            throw error;
        }
    }

    async collect(url , siteData ){

        // 构建站点文章数据结构
        const articleData = {
            site: siteData,
            notes: []
        };

        // 获取配置
        const siteConfig = this.getSiteConfig(url);

        // 执行收集器
        if (siteConfig.collect) {
            const collector = require('../collectors/collector');
            const collectorResult = await collector.scanAndFilterUrls(
                this.page,
                scrapedData,
                { site: siteData }
            );
            
            if (collectorResult && Array.isArray(collectorResult)) {
                articleData.notes = collectorResult;
            }
        }

        return articleData;
    }

    /**
     * 抓取页面内容
     * @param {string} url - 目标URL
     * @param {Object} siteData - 站点数据
     * @returns {Promise<Object>} 抓取结果
     */
    async scrape(url, siteData = {}) { 
        try {
            if (!this.browser) {
                await this.initBrowser();
            }

            // 获取配置
            const siteConfig = this.getSiteConfig(url);

            // 导航到页面
            await this.page.goto(url);

            // 执行所有动作并收集内容
            const scrapedData = {};
            if (siteConfig.actions) {
                for (const action of siteConfig.actions) {
                    const actionResult = await this.executeAction(action);
                    if (actionResult) {
                        Object.assign(scrapedData, actionResult);
                    }
                }
            }

            // 返回站点数据 
            return scrapedData;

        } catch (error) {
            console.error('抓取失败:', error);
            throw error;
        }
    }
}

module.exports = PageWebScraper;